import { createHash } from 'crypto';
import { CacheService } from './cacheService';
import { GeoLocation, GeoLocationConfig, IPAPIResponse } from '../types/geoLocation';

export class GeoLocationService {
  private static instance: GeoLocationService;
  private readonly cache: CacheService;
  private readonly config: GeoLocationConfig;
  private readonly localDB: Map<string, string>;

  private constructor() {
    this.config = {
      apiKey: process.env.GEOLOCATION_API_KEY,
      cacheEnabled: true,
      cacheTTL: 24 * 60 * 60, // 24 hours
      requestTimeout: 5000,    // 5 seconds
      retryAttempts: 3,
      retryDelay: 1000,       // 1 second
      apiEndpoint: process.env.GEOLOCATION_API_URL || 'https://api.ipapi.com/v1'
    };

    this.cache = CacheService.getInstance();
    this.localDB = this.initializeLocalDB();
  }

  public static getInstance(): GeoLocationService {
    if (!GeoLocationService.instance) {
      GeoLocationService.instance = new GeoLocationService();
    }
    return GeoLocationService.instance;
  }

  // 获取IP地理位置信息
  public async getLocation(ip: string): Promise<GeoLocation | null> {
    try {
      // 首先检查本地数据库
      const localCountry = this.checkLocalDB(ip);
      if (localCountry) {
        return {
          ip,
          country: localCountry,
          countryCode: localCountry,
          region: '',
          city: '',
          latitude: 0,
          longitude: 0,
          isp: '',
          timestamp: Date.now()
        };
      }

      // 检查缓存
      if (this.config.cacheEnabled) {
        const cached = await this.cache.get<GeoLocation>(`geo:${ip}`);
        if (cached) {
          return cached;
        }
      }

      // 调用API
      const location = await this.fetchLocation(ip);
      
      // 缓存结果
      if (this.config.cacheEnabled && location) {
        await this.cache.set(`geo:${ip}`, location, this.config.cacheTTL);
      }

      return location;
    } catch (error) {
      console.error('Failed to get location for IP:', ip, error);
      return null;
    }
  }

  // 批量获取地理位置信息
  public async batchGetLocations(ips: string[]): Promise<Map<string, GeoLocation | null>> {
    const results = new Map<string, GeoLocation | null>();
    const uncachedIps = new Set<string>();

    // 首先检查缓存
    if (this.config.cacheEnabled) {
      const cacheKeys = ips.map(ip => `geo:${ip}`);
      const cachedResults = await this.cache.mget<GeoLocation>(cacheKeys);

      for (const [key, value] of Object.entries(cachedResults)) {
        const ip = key.replace('geo:', '');
        if (value) {
          results.set(ip, value);
        } else {
          uncachedIps.add(ip);
        }
      }
    } else {
      ips.forEach(ip => uncachedIps.add(ip));
    }

    // 对未缓存的IP进行批量查询
    if (uncachedIps.size > 0) {
      const fetchPromises = Array.from(uncachedIps).map(ip => this.fetchLocation(ip));
      const fetchedResults = await Promise.allSettled(fetchPromises);

      fetchedResults.forEach((result, index) => {
        const ip = Array.from(uncachedIps)[index];
        if (result.status === 'fulfilled' && result.value) {
          results.set(ip, result.value);
          
          // 缓存新获取的结果
          if (this.config.cacheEnabled) {
            this.cache.set(`geo:${ip}`, result.value, this.config.cacheTTL);
          }
        } else {
          results.set(ip, null);
        }
      });
    }

    return results;
  }

  // 检查IP是否来自特定国家
  public async isFromCountry(ip: string, countryCode: string): Promise<boolean> {
    const location = await this.getLocation(ip);
    return location?.countryCode === countryCode.toUpperCase();
  }

  // 私有方法
  private initializeLocalDB(): Map<string, string> {
    const db = new Map<string, string>();
    
    // 添加内部网络
    db.set('127.0.0.1', 'LOCAL');
    db.set('::1', 'LOCAL');
    
    // 添加私有网络
    ['10.', '172.16.', '172.17.', '172.18.', '172.19.', '172.20.', 
     '172.21.', '172.22.', '172.23.', '172.24.', '172.25.', '172.26.',
     '172.27.', '172.28.', '172.29.', '172.30.', '172.31.', '192.168.'].forEach(prefix => {
      db.set(prefix, 'PRIVATE');
    });

    return db;
  }

  private checkLocalDB(ip: string): string | null {
    // 检查精确匹配
    if (this.localDB.has(ip)) {
      return this.localDB.get(ip)!;
    }

    // 检查前缀匹配
    for (const [prefix, value] of this.localDB.entries()) {
      if (ip.startsWith(prefix)) {
        return value;
      }
    }

    return null;
  }

  private async fetchLocation(ip: string): Promise<GeoLocation | null> {
    let attempts = 0;

    while (attempts < this.config.retryAttempts) {
      const controller = new AbortController();
      const timeout = setTimeout(() => {
        controller.abort();
      }, this.config.requestTimeout);

      try {
        const response = await fetch(
          `${this.config.apiEndpoint}/${ip}?access_key=${this.config.apiKey}`,
          {
            signal: controller.signal
          }
        );

        clearTimeout(timeout);

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }

        const data = await response.json() as IPAPIResponse;
        return {
          ip: data.ip,
          country: data.country_name,
          countryCode: data.country_code,
          region: data.region_name,
          city: data.city,
          latitude: data.latitude,
          longitude: data.longitude,
          isp: data.isp,
          timestamp: Date.now()
        };
      } catch (error) {
        attempts++;
        if (attempts < this.config.retryAttempts) {
          await new Promise(resolve => 
            setTimeout(resolve, this.config.retryDelay * attempts)
          );
        } else {
          throw error;
        }
      }
    }

    return null;
  }

  // IP数据库预热
  public async warmupLocalDB(): Promise<GeoLocationService> {
    try {
      // 预加载常用地区的IP数据
      const commonCountries = ['CN', 'US', 'JP', 'KR', 'SG'];
      const tasks = commonCountries.map(async country => {
        const key = `geo:country:${country}`;
        if (!(await this.cache.get(key))) {
          await this.cache.set(key, {
            countryCode: country,
            ranges: await this.loadIPRanges(country)
          });
        }
      });

      await Promise.all(tasks);
      return this;
    } catch (error) {
      console.error('Failed to warmup local DB:', error);
      return this;
    }
  }

  // 加载IP范围数据
  private async loadIPRanges(countryCode: string): Promise<string[]> {
    // 这里应该从IP数据库加载数据
    // 目前返回示例数据
    return this.getMockIPRanges(countryCode);
  }

  // 获取模拟数据
  private getMockIPRanges(countryCode: string): string[] {
    const ranges: Record<string, string[]> = {
      'CN': ['1.0.0.0/8', '14.0.0.0/8', '27.0.0.0/8'],
      'US': ['3.0.0.0/8', '4.0.0.0/8', '6.0.0.0/8'],
      'JP': ['43.0.0.0/8', '45.0.0.0/8'],
      'KR': ['27.0.0.0/8', '58.0.0.0/8'],
      'SG': ['8.0.0.0/8', '11.0.0.0/8']
    };
    return ranges[countryCode] || [];
  }

  // 辅助方法：规范化IP地址
  public normalizeIP(ip: string): string {
    // 处理IPv4-mapped IPv6地址
    if (ip.startsWith('::ffff:')) {
      return ip.substring(7);
    }
    return ip;
  }

  // 辅助方法：计算IP地址的哈希值
  public hashIP(ip: string): string {
    return createHash('md5').update(ip).digest('hex');
  }
}
